% render "layouts/guides.html" do

The majority of patterns are concerned with reading and writing to registers to make the
DUT do something and consequently 
more than 90% of your time developing an Origen test application should be concerned with
writing code at the register level.

All test [transaction drivers](<%= path "plugins/#Transaction_Protocol_Drivers" %>)
such as the [ARM Debug driver](http://origen-sdk.org/arm_debug) are expected to support the basic
Origen register API. This allows the test engineer to develop code at register level and
independently of the underlying test communication protocol which can be easily changed
to something else as required.

#### Basic Concept

See the [Registers](<%= path "guides/models/registers" %>) section of
the [Models](<%= path "guides/models/introduction" %>) guide for details and examples
of how to add and manipulate register states within your model logic.

Interaction with the registers should be limited to the [model's controller](<%= path "guides/controllers/introduction" %>)
- no one else should reach into a model to manipulate its register state (this is not prevented
but recommended), and the controllers should instead expose interface methods for the outside
world to use. Internally such methods will work by manipulating registers in a sequence.

The <code>read!</code> and <code>write!</code> methods when called on a register (or bit) will
automatically fire off a request for the register to be written using the following
rules:

* If the model or controller that owns the register implements a <code>write_register</code> method
  then call this with the target register passed in as the argument
* Otherwise if the object that owns the register implements an <code>owner</code> method,
  then see if the object that this returns provides a suitable <code>write_register</code>
  method
* If not then if a currently instantiated model includes the <code>Origen::TopLevel</code>
  module then see if it (or its controller) has a <code>write_register</code> method available
* If the request has still not been fulfilled then raise an error  

Read works in the same way except that it looks for a method called <code>read_register</code>.

What this all means is that within a controller you will build your
test logic from methods that look like this:

~~~ruby
def measure_vref(setting=nil)
  if setting
    ss "Measure the Vref voltage for setting #{setting}"
    vref.level.write(setting)
  else
    ss "Measure the default Vref voltage"
  end
  vref.test_enable.write!(1)
end

# From a pattern call like this:
Pattern.create do
  $dut.nvm.measure_vref(5)
end
~~~

The actual mechanism for how the registers are written is abstracted away from the
test logic itself and therefore the test logic is generally independent from the communication
protocol. This is a very powerful concept that allows plugins to be created that provide 
test sequences for a specific silicon module and these can then be re-used on DUTs that
employ completely different register access protocols.

#### Recommended Architecture

This is the recommended architecture for modern Origen applications that will lend itself
to working well within a plugin-based environment.

Define registers in the child models that own them and create test methods to manipulate them
in the controller:

~~~ruby
class NVM
  include Origen::Model

  def initialize
    reg :vref, 0x0003, size: 16 do |reg|
      reg.bit 15,   :test_enable
      reg.bit 7..0, :level
    end
  end
end

class NVMController
  include Origen::Controller

  def measure_vref(setting=nil)
    if setting
      ss "Measure the Vref voltage for setting #{setting}"
      vref.level.write(setting)
    else
      ss "Measure the default Vref voltage"
    end
    vref.test_enable.write!(1)
  end
end
~~~

Defer how to actually write the register to the top-level SoC controller and normally this would
be done via one of the available protocol plugins.
Here for example is an SoC which will write the register via the [Nexus protocol](http://origen-sdk.org/nexus):

~~~ruby
class MySoC
  include Origen::TopLevel  # Indicate to Origen that this model represents a top-level device object
end

class MySoCController
  include Origen::Controller
  include Nexus

  # Process register reads using the Nexus protocol
  def read_register(reg, options={})
    nexus.read_register(reg, options)
  end

  # As above for write requests
  def write_register(reg, options={})
    nexus.write_register(reg, options)
  end
end
~~~

And that is all that is required, Origen takes care of the hook up and the behind the scenes
communication to make it all work.

Another target may then instantiate a different SoC model which could use a completely different
protocol like [ARM Debug](http://origen-sdk.org/arm_debug), in which case the NVM test module would
still work although the generated pattern would look completely different.

#### Bit Level Access

All registers support bit level updates, we have seen an example of this already: 

~~~ruby
vref.test_enable.write!(1)
~~~

What this will do is update the value held by the given bits and then send the parent
register object to the <code>write_register</code> method for processing.
All other bits in the register will maintain the state that they had prior to this
operation commencing.

Since it is not generally possible to update only a subset of bits on a device the entire
register will still be updated on silicon.
However in the case of performing a bit-level read things get a bit more interesting.

The equivalent read operation will update the data values of the bits in the same way, but
it will also set a flag on those bits marking that they have been requested for read.

~~~ruby
vref.test_enable.read!(1)
~~~

The protocol driver can then look out for this flag when generating the readout vectors
and only enable a compare on the vectors that correspond to the bits marked for read.
Generally this takes a lot of the cognitive overhead out of writing patterns since you
can mentally disregard the state of all bits except the ones that you care about.

All standard Origen protocol plugins are expected to support this feature.

The same is true for store (meaning capture the value on the tester) or overlay operations:

~~~ruby
# Capture the value of the level bits
vref.level.store!

# Dynamically overlay the value written to the level bits
vref.level.overlay("vref_setting")
vref.write!
~~~

#### Combining Multiple Bit Level Accesses Into A Single Transaction

Multiple individual bits within a register can be accessed/manipulated within a single
transaction by making multiple bit-level calls to `read` (or `write`), followed by a single
register-level `read!` (or `write!`) operation:

~~~ruby
my_reg.my_bits_x.read(1)
my_reg.my_bits_z.read(0b101)
my_reg.read!
~~~

Sometimes you will see application code that combines the final transaction request with the
final bit-level operation, this will do the same thing as the above example:

~~~ruby
my_reg.my_bits_x.read(1)
my_reg.my_bits_z.read!(0b101)
~~~

If you prefer, an equivalent block form API is also available, this is equivalent to the above example:

~~~ruby
my_reg.read! do |reg|
  reg.my_bits_x.read(1)
  reg.my_bits_z.read(0b101)
end
~~~

#### Writing a Driver

Generally the code for an existing driver should be reviewed to see how to go about this,
the [JTAG driver](http://origen-sdk.org/jtag) would be a good example to look at,
but here are some basic pointers on good driver design.

All drivers should implement read and write register methods:

~~~ruby
def write_register(reg, options={})
end

def read_register(reg, options={})
end
~~~

The write methods are usually fairly simple, here is a basic example of how to do a parallel
and a serial protocol write:

~~~ruby
# Writing on a parallel port, let's say we have a 16-bit register and the
# data needs to be written on a pin group called data which is 8-bits
def write_register(reg, options={})
  pin(:din).drive(1)   # Let's say when this pin is high data is captured
  # Drive the data in MSB -> LSB order
  pins(:data).drive!(reg[15..8].data)
  pins(:data).drive!(reg[7..0].data)
  pin(:din).drive(0)   # Turn off capture
end

# Writing on a serial port, same as above only this time the :data port is
# only 1-bit wide
def read_register(reg, options={})
  pin(:din).drive(1)   # Let's say when this pin is high data is captured
  # Drive the data in MSB -> LSB order
  reg.shift_out_left do |bit|
    pin(:data).drive!(bit.data)
  end
  pin(:din).drive(0)   # Turn off capture
end
~~~

Read methods are usually a bit more involved to implement the bit-specific read and
capture operations.
Here is a parallel and serial protocol example which supports bit-level read operations:

~~~ruby
# Reading on a parallel port, let's say we have a 16-bit register and the
# data needs to be read on a pin group called data which is 8-bits
def write_register(reg, options={})
  pin(:dout).drive(1)   # Let's say when this pin is high data is presented

  # Compare the data in MSB
  8.times do |i|
    bit = reg.bit(i + 8)
    if bit.is_to_be_read?
      pins(:data)[i].assert(bit.data)
    else
      pins(:data)[i].dont_care
    end
  end
  $tester.cycle

  # Now the data in LSB
  8.times do |i|
    bit = reg.bit(i)
    if bit.is_to_be_read?
      pins(:data)[i].assert(bit.data)
    else
      pins(:data)[i].dont_care
    end
  end
  $tester.cycle

  pin(:dout).drive(0)   # Turn off read out
end

# Reading on a serial port, same as above only this time the :data port is
# only 1-bit wide
def read_register(reg, options={})
  pin(:dout).drive(1)   # Let's say when this pin is high data is presented

  # Drive the data in MSB -> LSB order
  reg.shift_out_left do |bit|
    if bit.is_to_be_read?
      pin(:data).assert!(bit.data)
    else
      pin(:data).dont_care!
    end
  end

  pin(:dout).drive(0)   # Turn off read out
end
~~~


% end
